home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / DMO / GargleDMO / Gargle.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  13.4 KB  |  409 lines

  1. //------------------------------------------------------------------------------
  2. // File: Gargle.cpp
  3. //
  4. // Desc: DirectShow sample code - implementation of CGargle class.
  5. //
  6. // Copyright (c) 2000-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include "stdafx.h"
  11. #define FIX_LOCK_NAME
  12. #include <dmo.h>
  13. #include <dmobase.h>
  14. #include <initguid.h> // needed to define GUID_TIME_REFERENCE from medparam.h
  15. #include <param.h>
  16. #include "Gargle.h"
  17. #include <uuids.h>    // DirectShow media type guids
  18.  
  19. #define DEFAULT_GARGLE_RATE 20
  20.  
  21. #define CHECK_PARAM(lo, hi) \
  22.     if (value < lo || value > hi) {return E_INVALIDARG;} ;
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. //
  26. // CGargle
  27. //
  28. CGargle::CGargle( ) :
  29.     m_ulShape(0),
  30.     m_ulGargleFreqHz(DEFAULT_GARGLE_RATE),
  31.     m_fDirty(true),
  32.     m_bInitialized(FALSE)
  33. {
  34.     m_pUnkMarshaler = NULL;
  35.  
  36.     #ifdef DEBUG
  37.         HRESULT hr = Init();
  38.         assert( SUCCEEDED( hr ) );
  39.     #else
  40.         Init();
  41.     #endif
  42. }
  43.  
  44. const MP_CAPS g_capsAll = MP_CAPS_CURVE_JUMP | MP_CAPS_CURVE_LINEAR | MP_CAPS_CURVE_SQUARE | MP_CAPS_CURVE_INVSQUARE | MP_CAPS_CURVE_SINE;
  45. static ParamInfo g_params[] =
  46. {
  47. //  index           type        caps        min,                        max,                        neutral,                    unit text,  label,          pwchText??
  48.     GFP_Rate,       MPT_INT,    g_capsAll,  GARGLE_FX_RATEHZ_MIN,      GARGLE_FX_RATEHZ_MAX,      20,                         L"Hz",      L"Rate",        L"",
  49.     GFP_Shape,      MPT_ENUM,   g_capsAll,  GARGLE_FX_WAVE_TRIANGLE,   GARGLE_FX_WAVE_SQUARE,     GARGLE_FX_WAVE_TRIANGLE,   L"",        L"WaveShape",   L"Triangle,Square",
  50. };
  51.  
  52. //////////////////////////////////////////////////////////////////////////////
  53. //
  54. // CGargle::Init
  55. //
  56. HRESULT CGargle::Init()
  57. {
  58.     HRESULT hr = S_OK;
  59.     if( !m_bInitialized )
  60.     {
  61.         hr = InitParams(1, &GUID_TIME_REFERENCE, 0, 0, sizeof(g_params)/sizeof(*g_params), g_params);
  62.     }
  63.  
  64.     if( SUCCEEDED( hr ) )
  65.     {
  66.         // compute the period
  67.         m_ulPeriod = m_ulSamplingRate / m_ulGargleFreqHz;
  68.         m_bInitialized = TRUE;
  69.     }
  70.     return hr;
  71. }
  72.  
  73. //////////////////////////////////////////////////////////////////////////////
  74. //
  75. // CGargle::Clone
  76. //
  77. HRESULT CGargle::Clone(IMediaObjectInPlace **ppCloned) 
  78. {
  79.     if (!ppCloned)
  80.         return E_POINTER;
  81.  
  82.     HRESULT hr = S_OK;
  83.     CGargle * pNewGargle = new CComObject<CGargle>;
  84.     if( !pNewGargle )
  85.         hr = E_OUTOFMEMORY;
  86.  
  87.     hr = pNewGargle->Init();
  88.  
  89.     IMediaObject * pCloned = NULL;
  90.     if( SUCCEEDED( hr ) )
  91.     {
  92.         IUnknown *pUnk;
  93.         hr = pNewGargle->QueryInterface( IID_IUnknown, (void **) &pUnk );
  94.         if( SUCCEEDED( hr ) )
  95.         {
  96.             hr = pUnk->QueryInterface( IID_IMediaObject, (void **) &pCloned );
  97.             pUnk->Release();
  98.         }
  99.     }
  100.     else
  101.     {
  102.         return hr;
  103.     }
  104.  
  105.     //
  106.     // Copy parameter control information
  107.     //
  108.     if (SUCCEEDED(hr))
  109.         hr = pNewGargle->CopyParamsFromSource((CParamsManager *) this);
  110.  
  111.     // Copy current parameter values
  112.     GargleFX params;
  113.     if (SUCCEEDED(hr))
  114.         hr = GetAllParameters(¶ms);
  115.  
  116.     if (SUCCEEDED(hr))
  117.         hr = pNewGargle->SetAllParameters(¶ms);
  118.  
  119.     if (SUCCEEDED(hr))
  120.     {
  121.         // Copy the input and output types
  122.         DMO_MEDIA_TYPE mt;
  123.         DWORD cInputStreams = 0;
  124.         DWORD cOutputStreams = 0;
  125.         GetStreamCount(&cInputStreams, &cOutputStreams);
  126.  
  127.         for (DWORD i = 0; i < cInputStreams && SUCCEEDED(hr); ++i)
  128.         {
  129.             hr = GetInputCurrentType(i, &mt);
  130.             if (hr == DMO_E_TYPE_NOT_SET)
  131.             {
  132.                 hr = S_OK; // great, don't need to set the cloned DMO
  133.             }
  134.             else if (SUCCEEDED(hr))
  135.             {
  136.                 hr = pCloned->SetInputType(i, &mt, 0);
  137.                 MoFreeMediaType( &mt );
  138.             }
  139.         }
  140.  
  141.         for (i = 0; i < cOutputStreams && SUCCEEDED(hr); ++i)
  142.         {
  143.             hr = GetOutputCurrentType(i, &mt);
  144.             if (hr == DMO_E_TYPE_NOT_SET)
  145.             {
  146.                 hr = S_OK; // great, don't need to set the cloned DMO
  147.             }
  148.             else if (SUCCEEDED(hr))
  149.             {
  150.                 hr = pCloned->SetOutputType(i, &mt, 0);
  151.                 MoFreeMediaType( &mt );
  152.             }
  153.         }
  154.  
  155.         if (SUCCEEDED(hr))
  156.             hr = pCloned->QueryInterface(IID_IMediaObjectInPlace, (void**)ppCloned);
  157.  
  158.         // Release the object's original ref.  If clone succeeded (made it through QI) then returned pointer
  159.         // has one ref.  If we failed, refs drop to zero, freeing the object.
  160.         pCloned->Release();
  161.     }
  162.     return hr;
  163. }
  164.  
  165.  
  166. //////////////////////////////////////////////////////////////////////////////
  167. //
  168. // CGargle::GetLatency
  169. //
  170. STDMETHODIMP CGargle::GetLatency(THIS_ REFERENCE_TIME *prt)
  171. {
  172.     *prt = 0;
  173.     return S_OK;
  174. }
  175.  
  176. //////////////////////////////////////////////////////////////////////////////
  177. //
  178. // CGargle::Discontinuity
  179. //
  180. HRESULT CGargle::Discontinuity() {
  181.     m_ulPhase = 0;
  182.     return NOERROR;
  183. }
  184.  
  185. //////////////////////////////////////////////////////////////////////////////
  186. //
  187. // CGargle::FBRProcess
  188. //
  189. HRESULT CGargle::FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut) {
  190.     if (!m_bInitialized)
  191.         return DMO_E_TYPE_NOT_SET;
  192.  
  193.     DWORD cSample, cChannel;
  194.     for (cSample = 0; cSample < cQuanta; cSample++) 
  195.     {
  196.         // If m_Shape is 0 (triangle) then we multiply by a triangular waveform
  197.         // that runs 0..Period/2..0..Period/2..0... else by a square one that
  198.         // is either 0 or Period/2 (same maximum as the triangle) or zero.
  199.         //
  200.         // m_Phase is the number of samples from the start of the period.
  201.         // We keep this running from one call to the next,
  202.         // but if the period changes so as to make this more
  203.         // than Period then we reset to 0 with a bang.  This may cause
  204.         // an audible click or pop (but, hey! it's only a sample!)
  205.         //
  206.         ++m_ulPhase;
  207.         if (m_ulPhase > m_ulPeriod)
  208.             m_ulPhase = 0;
  209.  
  210.         ULONG ulM = m_ulPhase;      // m is what we modulate with
  211.  
  212.         if (m_ulShape == 0) {   // Triangle
  213.             if (ulM > m_ulPeriod / 2)
  214.                 ulM = m_ulPeriod - ulM;  // handle downslope
  215.         } else {             // Square wave
  216.             if (ulM <= m_ulPeriod / 2)
  217.                 ulM = m_ulPeriod / 2;
  218.             else
  219.                 ulM = 0;
  220.         }
  221.  
  222.         for (cChannel = 0; cChannel < m_cChannels; cChannel++) {
  223.             if (m_b8bit) {
  224.                 // sound sample, zero based
  225.                 int i = pIn[cSample * m_cChannels + cChannel] - 128;
  226.                 // modulate
  227.                 i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
  228.                 // 8 bit sound uses 0..255 representing -128..127
  229.                 // Any overflow, even by 1, would sound very bad.
  230.                 // so we clip paranoically after modulating.
  231.                 // I think it should never clip by more than 1
  232.                 //
  233.                 if (i > 127)
  234.                     i = 127;
  235.                 if (i < -128)
  236.                     i = -128;
  237.                 // reset zero offset to 128
  238.                 pOut[cSample * m_cChannels + cChannel] = (unsigned char)(i + 128);
  239.    
  240.             } else {
  241.                 // 16 bit sound uses 16 bits properly (0 means 0)
  242.                 // We still clip paranoically
  243.                 //
  244.                 int i = ((short*)pIn)[cSample * m_cChannels + cChannel];
  245.                 // modulate
  246.                 i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
  247.                 // clip
  248.                 if (i > 32767)
  249.                     i = 32767;
  250.                 if (i < -32768)
  251.                     i = -32768;
  252.                 ((short*)pOut)[cSample * m_cChannels + cChannel] = (short)i;
  253.             }
  254.         }
  255.    }
  256.    return NOERROR;
  257. }
  258.  
  259. //////////////////////////////////////////////////////////////////////////////
  260. //
  261. // GetClassID
  262. //
  263. HRESULT CGargle::GetClassID(CLSID *pClsid)
  264. {
  265.     if (pClsid==NULL) {
  266.         return E_POINTER;
  267.     }
  268.     *pClsid = CLSID_Gargle;
  269.     return NOERROR;
  270.  
  271. } // GetClassID
  272.  
  273. //////////////////////////////////////////////////////////////////////////////
  274. //
  275. // CGargle::GetPages
  276. //
  277. HRESULT CGargle::GetPages(CAUUID * pPages)
  278. {
  279.     pPages->cElems = 1;
  280.     pPages->pElems = static_cast<GUID *>(CoTaskMemAlloc(sizeof(GUID)));
  281.     if (pPages->pElems == NULL)
  282.         return E_OUTOFMEMORY;
  283.  
  284.     *(pPages->pElems) = CLSID_GargDMOProp;
  285.  
  286.     return S_OK;
  287. }
  288.  
  289. //////////////////////////////////////////////////////////////////////////////
  290. //
  291. // CGargle::SetAllParameters
  292. //
  293. STDMETHODIMP CGargle::SetAllParameters(THIS_ LPCGargleFX pParm)
  294. {
  295.     HRESULT hr = S_OK;
  296.     
  297.     // Check that the pointer is not NULL
  298.     if (pParm == NULL) hr = E_POINTER;
  299.  
  300.     // Set the parameters
  301.     if (SUCCEEDED(hr)) hr = SetParam(GFP_Rate, static_cast<MP_DATA>(pParm->dwRateHz));
  302.     if (SUCCEEDED(hr)) hr = SetParam(GFP_Shape, static_cast<MP_DATA>(pParm->dwWaveShape));
  303.             
  304.     m_fDirty = true;
  305.     return hr;
  306. }
  307.  
  308. //////////////////////////////////////////////////////////////////////////////
  309. //
  310. // CGargle::GetAllParameters
  311. //
  312. STDMETHODIMP CGargle::GetAllParameters(THIS_ LPGargleFX pParm)
  313. {   
  314.     HRESULT hr = S_OK;
  315.     MP_DATA var;
  316.  
  317.     if (pParm == NULL)
  318.     {
  319.         return E_POINTER;
  320.     }
  321.  
  322. #define GET_PARAM_DWORD(x,y) \
  323.     if (SUCCEEDED(hr)) { \
  324.         hr = GetParam(x, &var); \
  325.         if (SUCCEEDED(hr)) pParm->y = (DWORD)var; \
  326.     }
  327.     
  328.     GET_PARAM_DWORD(GFP_Rate, dwRateHz);
  329.     GET_PARAM_DWORD(GFP_Shape, dwWaveShape);
  330.     
  331.     return hr;
  332. }
  333.  
  334. //////////////////////////////////////////////////////////////////////////////
  335. //
  336. // CGargle::SetParam
  337. //
  338. HRESULT CGargle::SetParamInternal(DWORD dwParamIndex, MP_DATA value, bool fSkipPasssingToParamManager)
  339. {
  340.     switch (dwParamIndex)
  341.     {
  342.     case GFP_Rate:
  343.         CHECK_PARAM(GARGLE_FX_RATEHZ_MIN,GARGLE_FX_RATEHZ_MAX);
  344.         m_ulGargleFreqHz = (unsigned)value;
  345.         if (m_ulGargleFreqHz < 1) m_ulGargleFreqHz = 1;
  346.         if (m_ulGargleFreqHz > 1000) m_ulGargleFreqHz = 1000;
  347.         
  348.         // Init is where m_ulPeriod is updated, so call it here
  349.         // Would be better to do this outside of Init though
  350.         Init();          
  351.         break;
  352.  
  353.     case GFP_Shape:
  354.         CHECK_PARAM(GARGLE_FX_WAVE_TRIANGLE,GARGLE_FX_WAVE_SQUARE);
  355.         m_ulShape = (unsigned)value;
  356.         break;
  357.     }
  358.  
  359.     // Let base class set this so it can handle all the rest of the param calls.
  360.     // Skip the base class if fSkipPasssingToParamManager.  This indicates that we're calling the function
  361.     //    internally using values that came from the base class -- thus there's no need to tell it values it
  362.     //    already knows.
  363.     return fSkipPasssingToParamManager ? S_OK : CParamsManager::SetParam(dwParamIndex, value);
  364. }
  365.  
  366. //////////////////////////////////////////////////////////////////////////////
  367. //
  368. // CGargle::Process
  369. //
  370. HRESULT CGargle::Process(ULONG ulQuanta, LPBYTE pcbData, REFERENCE_TIME rtStart, DWORD dwFlags)
  371. {
  372.     // Update parameter values from any curves that may be in effect.
  373.     // We pick up the current values stored in the CParamsManager helper for time rtStart. 
  374.  
  375.     // Note that we are using IMediaParams in a less than
  376.     // perfect way. We update at the beginning of every time slice instead of smoothly over the curve.
  377.     // This is okay for an effect like gargle as long as the time slice is consistently small (which 
  378.     // it conveniently is when hosted in DSound.)
  379.     // However, in the future we will update this sample to use a more appropriate and accurate
  380.     // mechanism.
  381.     // Here are some suggestions of how it can be done, with increasing degree of accuracy. Different
  382.     // types of effects and effect parameters require different levels of accuracy, so no solution is the best 
  383.     // solution for all (especially if you are concerned about CPU cost.)
  384.     // 1) Break the time slice up into mini pieces of some number of milliseconds
  385.     // each and run through all the steps in Process for each sub slice. This guarantees the
  386.     // stair stepping is small enough not to be noticable. This approach will work well for parameters
  387.     // that don't create an audible stair stepping noise (or "zipper") noise when controled in this way. 
  388.     // Control over volume, for example, does not work well.
  389.     // 2) Use the above mechanism, but pass the start and end values for each parameter to the
  390.     // processing engine. It, in turn, applies linear interpolation to each parameter. This results
  391.     // in a smooth approximation of the parameter curve and removes all but the most subtle aliasing noise.
  392.     // 3) Pass the curves directly to the processing engine, which accurately calculates each sample
  393.     // mathematically. This is obviously the best, but most complex and CPU intensive.
  394.  
  395.     this->UpdateActiveParams(rtStart, *this);
  396.  
  397.     DMO_MEDIA_TYPE mt;
  398.     HRESULT hr = GetInputCurrentType(0, &mt);
  399.     if( FAILED( hr ) )
  400.         return hr;
  401.  
  402.     // convert bytes to samples for the FBRProcess call
  403.     assert(mt.formattype == FORMAT_WaveFormatEx);
  404.     ulQuanta /= LPWAVEFORMATEX(mt.pbFormat)->nBlockAlign;
  405.     MoFreeMediaType( &mt );
  406.     return FBRProcess(ulQuanta, pcbData, pcbData);
  407. }
  408.  
  409.